﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace YunLian.Cache
{
    /// <summary>
    /// 缓存项的泛型实现
    /// </summary>
    public class CacheItem<T>:ICacheItem
    {
        public DateTime Created { get; }
        public ExpirationMode ExpirationMode { get; }
        public TimeSpan ExpirationTimeout { get; }
        public string Key { get; }
        public DateTime LastAccessed { get; set; }
        public string Region { get; }
        public T Value { get; }
        public bool UseExpirationDefault { get; } = true;

        public CacheItem(string key, T value)
            : this(key, null, value, null, null, null)
        {
        }
        public CacheItem(string key, string region, T value)
            : this(key, region, value, null, null, null)
        {
            if(string.IsNullOrWhiteSpace(region))throw new ArgumentNullException(nameof(region));
        }
        public CacheItem(string key, T value, ExpirationMode expiration, TimeSpan timeout)
            : this(key, null, value, expiration, timeout, null, null, false)
        {
        }
        public override string ToString()
        {
            return !string.IsNullOrWhiteSpace(Region) ?
                $"'{Region}:{Key}', exp:{ExpirationMode.ToString()} {ExpirationTimeout}, lastAccess:{LastAccessed}"
                : $"'{Key}', exp:{ExpirationMode.ToString()} {ExpirationTimeout}, lastAccess:{LastAccessed}";
        }
        public CacheItem(string key, string region, T value, ExpirationMode expiration, TimeSpan timeout)
            : this(key, region, value, expiration, timeout, null, null, false)
        {
            if (string.IsNullOrWhiteSpace(region)) throw new ArgumentNullException(nameof(region));
        }

        /// <summary>
        /// 
        /// </summary>
        protected CacheItem()
        {
        }
        private CacheItem(string key, string region, T value, ExpirationMode? expiration, TimeSpan? timeout, DateTime? created, DateTime? lastAccessed = null, bool expirationDefaults = true)
        {
           if(string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
            if (value==null) throw new ArgumentNullException(nameof(value));
           

            Key = key;
            Region = region;
            Value = value;
            ExpirationMode = expiration ?? ExpirationMode.None;
            ExpirationTimeout = ExpirationMode == ExpirationMode.None ? TimeSpan.Zero : timeout ?? TimeSpan.Zero;
            UseExpirationDefault = expirationDefaults;

            
            if (ExpirationTimeout.TotalDays > 365)
            {
                throw new ArgumentOutOfRangeException(nameof(timeout), "最大缓存时间不能能超过365天");
            }

            if (ExpirationMode != ExpirationMode.None && ExpirationTimeout <= TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(timeout), "请设置缓存过期时间");
            }

            

            Created = created ?? DateTime.Now;
            LastAccessed = lastAccessed ?? DateTime.Now;
        }
        internal CacheItem<T> WithExpiration(ExpirationMode mode,TimeSpan timeout,bool useHandleDefault=true)=>
            new CacheItem<T>(Key,Region,Value,mode,timeout,mode==ExpirationMode.Absolute?DateTime.Now : Created,LastAccessed,useHandleDefault);
        public CacheItem<T> WithAbsoluteExpiration(DateTimeOffset absoluteExpiration)
        {
            var timeout = absoluteExpiration - DateTimeOffset.Now;
            if (timeout <= TimeSpan.Zero)
            {
                throw new ArgumentException("过期时间必须大于0", nameof(absoluteExpiration));
            }

            return WithExpiration(ExpirationMode.Absolute, timeout, false);
        }
        public CacheItem<T> WithAbsoluteExpiration(TimeSpan absoluteExpiration)
        {
            if (absoluteExpiration <= TimeSpan.Zero)
            {
                throw new ArgumentException("过期时间必须大于0", nameof(absoluteExpiration));
            }

            return WithExpiration(ExpirationMode.Absolute, absoluteExpiration, false);
        }
        public CacheItem<T> WithSlidingExpiration(TimeSpan slidingExpiration)
        {
            if (slidingExpiration <= TimeSpan.Zero)
            {
                throw new ArgumentException("过期时间必须大于0", nameof(slidingExpiration));
            }

            return WithExpiration(ExpirationMode.Sliding, slidingExpiration, false);
        }
        public CacheItem<T> WithNoExpiration() =>
            new CacheItem<T>(Key, Region, Value, ExpirationMode.None, TimeSpan.Zero, Created, LastAccessed, false);
        public CacheItem<T> WithDefaultExpiration() =>
            new CacheItem<T>(Key, Region, Value, ExpirationMode.None, TimeSpan.Zero, Created, LastAccessed);
        public CacheItem<T> WithValue(T value) =>
            new CacheItem<T>(Key, Region, value, ExpirationMode, ExpirationTimeout, Created, LastAccessed, UseExpirationDefault);
        public CacheItem<T> WithCreated(DateTime created) =>
            new CacheItem<T>(Key, Region, Value, ExpirationMode, ExpirationTimeout, created, LastAccessed, UseExpirationDefault);

    }
}
